package com.ssm.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.ssm.auth.common.enums.AuthUserStatusEnum;
import com.ssm.auth.common.enums.IsDeletedFlagEnum;
import com.ssm.auth.domain.bo.AuthUserBO;
import com.ssm.auth.domain.constants.AuthConstant;
import com.ssm.auth.domain.convert.AuthUserBoConvert;
import com.ssm.auth.domain.redis.RedisUtil;
import com.ssm.auth.domain.service.AuthPermissionDomainService;
import com.ssm.auth.domain.service.AuthUserDomainService;
import com.ssm.auth.infra.basic.entity.*;
import com.ssm.auth.infra.basic.service.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

@Service
@Log4j2
public class AuthUserDomainServiceImpl implements AuthUserDomainService {
    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthRoleService authRoleService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private AuthRolePermissionService authRolePermissionService;

    @Resource
    private AuthPermissionService authPermissionService;

    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";  //权限key
    private String authRolePrefix = "auth.role";  //角色key

    private static final String[] IKUN =  {"唱", "跳", "rap", "篮球"};

    private Random random = new Random();
    private static final String LOGIN_PREFIX = "loginCode"; //login验证码前缀


    /**
     * 用户注册(先查询，该用户存在，则直接返回，避免重复注册)
     * 密码加密：md5BySalt 加盐，通过自定义字符串与原文进行二次加密，这样生成出的密码不易被撞库破解。
     * @param authUserBO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {
        if(log.isInfoEnabled()) {
            log.info("AuthUserDomainService.register.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBoConvert.INSTANCE.userBoToUser(authUserBO);
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        List<AuthUser> authUsers = authUserService.queryByCondition(authUser);
        if(authUsers.size() > 0) { //已存在用户
            return true;
        }
        //密码不为空时，设置密码。（后续对接验证码后，就用不到密码了）
        if(StringUtils.isNotBlank(authUser.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), "ssm030927"));
        }

        if (StringUtils.isBlank(authUser.getAvatar())) {
            authUser.setAvatar("http://117.72.118.73:9000/kunkunclub-bucket/icon/kk.jpg");
        }
        if (StringUtils.isBlank(authUser.getNickName())) {
            authUser.setNickName(IKUN[random.nextInt(4)] + new Random().nextInt(1000));
        }


        //设置未删除、启用状态
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        int count = authUserService.insert(authUser);

        /**
         * 建立一个初步的角色关联(新注册的用户默认角色为普通用户)
         * 1. 手动添加normal_user普通角色属性， 根据角色名称，查询角色id
         * 2. 插入用户时，开启了主键回显，根据用户id和角色id，往user_role表中插入数据
         */
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstant.NORMAL_USER);
        AuthRole role = authRoleService.queryByCondition(authRole);
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(authUser.getId());
        authUserRole.setRoleId(role.getId());
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        authUserRoleService.insert(authUserRole);
        /**
         * 要把当前用户的决策和权限都刷到redis中
         * 1.构建缓存key：前缀 + 用户名称
         * 2. 角色缓存中存放authRole对象
         * 3. 权限缓存中存放authPermission对象(需先根据role查询)
         */
        String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());
        List<AuthRole> authRoles = new LinkedList<>();
        authRoles.add(role);
        redisUtil.set(roleKey,  new Gson().toJson(authRoles));

        //根据roleId查询Permission集合
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(role.getId());
        authRolePermission.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        List<AuthRolePermission> authRolePermissions  = authRolePermissionService.queryByCondition(authRolePermission);
        //获取PermissionId
        List<Long> permissionIds = authRolePermissions.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());
        //根据permissionIds批量查询Permission
        List<AuthPermission> authPermissions = authPermissionService.queryByPermissionList(permissionIds);

        String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());
        redisUtil.set(permissionKey, new Gson().toJson(authPermissions));
        return count > 0;
    }

    /**
     * 更新用户信息
     * @param authUserBO
     * @return
     */
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        if(log.isInfoEnabled()) {
            log.info("AuthUserDomainService.update.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBoConvert.INSTANCE.userBoToUser(authUserBO);
        int count = authUserService.updateByUserName(authUser);

        //有任何的更新都要与缓存同步修改
        return count > 0;
    }

    /**
     * 删除用户
     * @param authUserBO
     * @return
     */
    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        if(log.isInfoEnabled()) {
            log.info("AuthUserDomainService.delete.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBoConvert.INSTANCE.userBoToUser(authUserBO);
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        int count = authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 用户启用/禁用
     * @param authUserBO
     * @return
     */
    @Override
    public Boolean changeStatus(AuthUserBO authUserBO) {
        if(log.isInfoEnabled()) {
            log.info("AuthUserDomainService.changeStatus.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBoConvert.INSTANCE.userBoToUser(authUserBO);
        int count = authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 验证码登录
     * @param validCode
     * @return
     */
    @Override
    public SaTokenInfo doLogin(String validCode) {
        //拿到用户在该公众号的唯一id
        String openId = redisUtil.get(redisUtil.buildKey(LOGIN_PREFIX, validCode));
        if(StringUtils.isBlank(openId)) {
            return null;
        }
        //进行用户注册
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        this.register(authUserBO);

        StpUtil.login(openId);
        return StpUtil.getTokenInfo();
    }

    /**
     * 查询个人信息
     * @param authUserBO
     * @return
     */
    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        if(log.isInfoEnabled()) {
            log.info("AuthUserDomainService.getUserInfo.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBoConvert.INSTANCE.userBoToUser(authUserBO);
        List<AuthUser> authUsers = authUserService.queryByCondition(authUser);
        if(CollectionUtils.isEmpty(authUsers)) {
            return new AuthUserBO();
        }
        AuthUser authUser1 = authUsers.get(0);
        AuthUserBO authUserBO1 = AuthUserBoConvert.INSTANCE.UserToUserBo(authUser1);
        return authUserBO1;
    }

    @Override
    public List<AuthUserBO> listUserInfoByIds(List<String> userNameList) {
        List<AuthUser> userList = authUserService.listUserInfoByIds(userNameList);
        if (CollectionUtils.isEmpty(userList)) {
            return Collections.emptyList();
        }
        return AuthUserBoConvert.INSTANCE.UsersToUserBos(userList);
    }

}
